﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LastfmNet.Api;
using System.Net;
using System.Reflection;
using System.IO;
using System.Net.Cache;
using System.Globalization;
namespace LastfmNet
{
    public class Lastfm
    {
        // TODO: Limit queries to 2 for 1 second
        public const string ApiUrl = "http://ws.audioscrobbler.com/2.0/";
        public static readonly string[] Errors = new string[] {
            "No error",
            "This error does not exist",
            "Invalid service -This service does not exist",
            "Invalid Method - No method with that name in this package",
            "Authentication Failed - You do not have permissions to access the service",
            "Invalid format - This service doesn't exist in that format",
            "Invalid parameters - Your request is missing a required parameter",
            "Invalid resource specified",
            "Operation failed - Most likely the backend service failed. Please try again.",
            "Invalid session key - Please re-authenticate",
            "Invalid API key - You must be granted a valid key by last.fm",
            "Service Offline - This service is temporarily offline. Try again later.",
            "Subscribers Only - This station is only available to paid last.fm subscribers",
            "Invalid method signature supplied",
            "Unauthorized Token - This token has not been authorized",
            "This item is not available for streaming.",
            "The service is temporarily unavailable, please try again.",
            "Login: User requires to be logged in",
            "Trial Expired - This user has no free radio plays left. Subscription required.",
            "This error does not exist",
            "Not Enough Content - There is not enough content to play this station",
            "Not Enough Members - This group does not have enough members for radio",
            "Not Enough Fans - This artist does not have enough fans for for radio",
            "Not Enough Neighbours - There are not enough neighbours for radio",
            "No Peak Radio - This user is not allowed to listen to radio during peak usage",
            "Radio Not Found - Radio station not found",
            "API Key Suspended - This application is not allowed to make requests to the web services",
            "Deprecated - This type of request is no longer supported",
        };
        private LastfmSession session;
        private LastfmNet.Api.AlbumApi album;
        private LastfmNet.Api.ArtistApi artist;
        private LastfmNet.Api.AuthApi auth;
        private LastfmNet.Api.ChartApi chart;
        private LastfmNet.Api.EventApi @event;
        private LastfmNet.Api.GeoApi geo;
        private LastfmNet.Api.GroupApi group;
        private LastfmNet.Api.LibraryApi library;
        private LastfmNet.Api.PlaylistApi playlist;
        private LastfmNet.Api.RadioApi radio;
        private LastfmNet.Api.TagApi tag;
        private LastfmNet.Api.TasteometerApi tasteometer;
        private LastfmNet.Api.TrackApi track;
        private LastfmNet.Api.UserApi user;
        private LastfmNet.Api.VenueApi venue;
        private string userAgent;
        private int requestTimeout;

        public Lastfm(string apiKey, string apiSecret)
        {
            session = new LastfmSession(this, apiKey, apiSecret);
            album = new AlbumApi(this);
            artist = new ArtistApi(this);
            auth = new AuthApi(this);
            chart = new ChartApi(this);
            @event = new EventApi(this);
            geo = new GeoApi(this);
            group = new GroupApi(this);
            library = new LibraryApi(this);
            playlist = new PlaylistApi(this);
            radio = new RadioApi(this);
            tag = new TagApi(this);
            tasteometer = new TasteometerApi(this);
            track = new TrackApi(this);
            user = new UserApi(this);
            venue = new VenueApi(this);
            userAgent = string.Format(CultureInfo.InvariantCulture, "{0}/{1} ({2}; {3}; .NET CLR {4}; {5}; {6}.{7})",
                Assembly.GetExecutingAssembly().GetName().Name,
                Assembly.GetExecutingAssembly().GetName().Version,
                Environment.OSVersion.ToString(),
                Environment.Is64BitOperatingSystem ? "x64" : "x86",
                Environment.Version.ToString(),
                Environment.MachineName,
                Environment.UserName,
                Environment.ProcessorCount);
            requestTimeout = 20000;
        }

        public void Authenticate(string lastfmUsername, string lastfmPassword)
        {
            Session.Authenticate(lastfmUsername, lastfmPassword);
        }

        public LastfmResponse MakeRequest(LastfmParameters lastfmParameters, HttpMethod httpMethod = HttpMethod.Get)
        {
            string response = GetResponseString(lastfmParameters, httpMethod);
            LastfmResponse lastfmResponse = new LastfmResponse(response);
            return lastfmResponse;
        }

        private string GetResponseString(LastfmParameters lastfmParameters, HttpMethod httpMethod = HttpMethod.Get)
        {
            if (lastfmParameters == null)
            {
                throw new NullReferenceException("Last.fm parameters must not be null!");
            }
            if (string.IsNullOrEmpty(lastfmParameters.Method))
            {
                throw new ArgumentException("You must specify a Last.fm method!");
            }
            string requestUriString;
            if (httpMethod == HttpMethod.Get)
            {
                requestUriString = string.Format(CultureInfo.InvariantCulture, "{0}?{1}", ApiUrl, lastfmParameters);
            }
            else if (httpMethod == HttpMethod.Post)
            {
                requestUriString = ApiUrl;
            }
            else
            {
                throw new ArgumentException("Accessing Last.fm API must be only via GET and POST http requests!");
            }
            // TODO: catch all possible exceptions in the code below
            HttpWebRequest httpRequest = (HttpWebRequest)HttpWebRequest.Create(requestUriString);
            httpRequest.KeepAlive = false;
            httpRequest.ProtocolVersion = new Version("1.0");
            httpRequest.UserAgent = UserAgent;
            httpRequest.Timeout = RequestTimeout;
            httpRequest.Method = "GET";
            if (httpMethod == HttpMethod.Post)
            {
                httpRequest.Method = "POST";
                httpRequest.ContentType = "application/x-www-form-urlencoded";
                byte[] postDataBytes = lastfmParameters.ToByteArray();
                httpRequest.ContentLength = postDataBytes.Length;
                using (Stream requestStream = httpRequest.GetRequestStream())
                {
                    requestStream.Write(postDataBytes, 0, postDataBytes.Length);
                }
            }
            HttpWebResponse httpResponse;
            try
            {
                httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            }
            catch (Exception ex)
            {
                throw new LastfmNetException(string.Format(CultureInfo.InvariantCulture, "Request to last.fm failed! ({0})", ex.Message), ex);
            }
            if (httpResponse.StatusCode != HttpStatusCode.OK)
            {
                throw new LastfmNetException(string.Format(CultureInfo.InvariantCulture, "Request to Last.fm returned status code: {0}!", httpResponse.StatusCode));
            }
            string responseString = "";
            using (StreamReader responseStream = new StreamReader(httpResponse.GetResponseStream(), Encoding.UTF8))
            {
                responseString = responseStream.ReadToEnd();
            }
            return responseString;
        }

        public LastfmSession Session
        {
            get
            {
                return session;
            }
        }

        public LastfmNet.Api.AlbumApi Album
        {
            get
            {
                return album;
            }
        }

        public LastfmNet.Api.ArtistApi Artist
        {
            get
            {
                return artist;
            }
        }

        public LastfmNet.Api.AuthApi Auth
        {
            get
            {
                return auth;
            }
        }

        public LastfmNet.Api.ChartApi Chart
        {
            get
            {
                return chart;
            }
        }

        public LastfmNet.Api.EventApi Event
        {
            get
            {
                return @event;
            }
        }

        public LastfmNet.Api.GeoApi Geo
        {
            get
            {
                return geo;
            }
        }

        public LastfmNet.Api.GroupApi Group
        {
            get
            {
                return group;
            }
        }

        public LastfmNet.Api.LibraryApi Library
        {
            get
            {
                return library;
            }
        }

        public LastfmNet.Api.PlaylistApi Playlist
        {
            get
            {
                return playlist;
            }
        }

        public LastfmNet.Api.RadioApi Radio
        {
            get
            {
                return radio;
            }
        }

        public LastfmNet.Api.TagApi Tag
        {
            get
            {
                return tag;
            }
        }

        public LastfmNet.Api.TasteometerApi TasteoMeter
        {
            get
            {
                return tasteometer;
            }
        }

        public LastfmNet.Api.TrackApi Track
        {
            get
            {
                return track;
            }
        }

        public LastfmNet.Api.UserApi User
        {
            get
            {
                return user;
            }
        }

        public LastfmNet.Api.VenueApi Venue
        {
            get
            {
                return venue;
            }
        }

        public string UserAgent
        {
            get
            {
                return userAgent;
            }
            set
            {
                userAgent = value;
            }
        }

        public int RequestTimeout
        {
            get
            {
                return requestTimeout;
            }
            set
            {
                requestTimeout = value;
            }
        }
    }
}
